﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NEH_SA.Infrastruktura;
using NEH_SA.Model;

namespace NEH_SA.Logika
{
    public class Program
    {

        public static List<Problem> WczytajDane(string ścieżka)
        {
            var result = Dane.Wczytaj(ścieżka).Take(60).ToList();
            DataTransition.Log(string.Format("Wczytano dane. Liczba problemów: {0}", result.Count));
            DataTransition.LiczbaProblemówChange(result.Count);
            return result;
        }

        public static void ZapiszRozwiązanie(List<Rozwiązanie> rozwiązania, string filePath)
        {
            if (rozwiązania != null && rozwiązania.Any())
            {
                Dane.Zapisz(rozwiązania.OrderBy(r => r.NazwaProblemu).ThenBy(r => r.TypRozwiązania).ToList(), filePath);
                DataTransition.Log(string.Format("Zapisano {0} rozwiązań do pliku {1}", rozwiązania.Count, filePath));
            }
            else
            {
                DataTransition.Log(string.Format("Nie zapisano do pliku, gdyż liczba rozwiązań wynosi 0"));
            }
        }

        public static IEnumerable<Rozwiązanie> ObliczNehem(List<Problem> problemyLista)
        {
            DataTransition.Log("Rozpoczęcie obliczeń algorytmem NEH");
            DataTransition.VisibilityChange(true);
            var przed = DateTime.Now;
            var nehStandard = new NEH.NehStandard(problemyLista);
            var result = nehStandard.WygenerujeRozwiązania().ToList();
            var po = DateTime.Now;
            var różnica = po - przed;
            DataTransition.Log(string.Format("Obliczono rozwiązania dla {0} problemów. Co zajęło {1:00}:{2:00}:{3:000}", problemyLista.Count,
                                                różnica.Minutes, różnica.Seconds, różnica.Milliseconds));
            DataTransition.VisibilityChange(false);
            return result;
        }

        public static List<Rozwiązanie> ObliczSa(List<Problem> problemyLista, CancellationToken token)
        {
            DataTransition.VisibilityChange(true);
            if (problemyLista == null)
            {
                return new List<Rozwiązanie>();
            }
            var result = new List<Rozwiązanie>();
            var parallelOptions = new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = Environment.ProcessorCount * 2 };
            try
            {
                DataTransition.Log("Rozpoczęcie obliczeń algorytmem SA");
                var przed = DateTime.Now;
                Parallel.ForEach(problemyLista, parallelOptions, problem =>
                                                                     {
                                                                         DataTransition.AddProblem(problem.Nazwa);
                                                                         DataTransition.Log(string.Format("Sa Start: {0}", problem.Nazwa));
                                                                         result.Add(SA.SimulatedAnnealing.WygenerujeRozwiązanie(problem));
                                                                         DataTransition.RemoveProblem(problem.Nazwa);
                                                                         DataTransition.Log(string.Format("Sa End: {0}", problem.Nazwa));
                                                                         DataTransition.PostępChange();
                                                                     });
                var po = DateTime.Now;
                var różnica = po - przed;
                DataTransition.Log(string.Format("Obliczono rozwiązania dla {0} problemów. Co zajęło {1:00}:{2:00}:{3:000}", problemyLista.Count,
                                                 różnica.Minutes, różnica.Seconds, różnica.Milliseconds));
            }
            catch
            {
                DataTransition.Log("Anulowano działanie algorytmu SA");
                return new List<Rozwiązanie>();
            }
            finally
            {
                DataTransition.VisibilityChange(false);
            }
            return result.OrderBy(rozwiązanie => rozwiązanie.NazwaProblemu).ToList();
        }

        public static IEnumerable<Rozwiązanie> ObliczAco(List<Problem> problemyLista, CancellationToken token)
        {
            var nehStandard = new NEH.NehStandard(problemyLista);
            var nehResult = nehStandard.WygenerujeRozwiązania().ToList();
            var zipped = nehResult.Zip(problemyLista, (rozwiązanie, problem) => new { nehResult = rozwiązanie, problem });
            var result = new List<Rozwiązanie>();
            var parallelOptions = new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = Environment.ProcessorCount * 2 };
            DataTransition.VisibilityChange(true);
            try
            {
                var przed = DateTime.Now;
                Parallel.ForEach(zipped, parallelOptions, problem =>
                {
                    DataTransition.AddProblem(problem.problem.Nazwa);
                    DataTransition.Log(string.Format("Aco Start: {0}", problem.problem.Nazwa));
                    result.Add(ACO.Paco.WygenerujRozwiązanie(problem.nehResult, problem.problem));
                    DataTransition.RemoveProblem(problem.problem.Nazwa);
                    DataTransition.Log(string.Format("Aco End: {0}", problem.problem.Nazwa));
                    DataTransition.PostępChange();
                });
                var po = DateTime.Now;
                var różnica = po - przed;
                DataTransition.Log(string.Format("Obliczono rozwiązania dla {0} problemów. Co zajęło {1:00}:{2:00}:{3:000}", problemyLista.Count,
                                                 różnica.Minutes, różnica.Seconds, różnica.Milliseconds));
            }
            catch (Exception)
            {
                DataTransition.Log("Anulowano działanie algorytmu ACO");
                return new List<Rozwiązanie>();
            }
            finally
            {
                DataTransition.VisibilityChange(false);
            }


            return result.OrderBy(rozwiązanie => rozwiązanie.NazwaProblemu);
        }


        public static IEnumerable<Rozwiązanie> ObliczTabu(List<Problem> problemyLista, CancellationToken token)
        {
            var nehStandard = new NEH.NehStandard(problemyLista);
            var nehResult = nehStandard.WygenerujeRozwiązania().ToList();
            var zipped = nehResult.Zip(problemyLista, (rozwiązanie, problem) => new { nehResult = rozwiązanie, problem });
            var result = new List<Rozwiązanie>();
            var parallelOptions = new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = Environment.ProcessorCount * 2 };
            DataTransition.VisibilityChange(true);
            try
            {
                var przed = DateTime.Now;
                Parallel.ForEach(zipped, parallelOptions, problem =>
                {
                    DataTransition.AddProblem(problem.problem.Nazwa);
                    DataTransition.Log(string.Format("Tabu Start: {0}", problem.problem.Nazwa));
                    result.Add(TABU.Tabu.WygenerujRozwiązanie(problem.nehResult, problem.problem));
                    DataTransition.RemoveProblem(problem.problem.Nazwa);
                    DataTransition.Log(string.Format("Tabu End: {0}", problem.problem.Nazwa));
                    DataTransition.PostępChange();
                });
                var po = DateTime.Now;
                var różnica = po - przed;
                DataTransition.Log(string.Format("Obliczono rozwiązania dla {0} problemów. Co zajęło {1:00}:{2:00}:{3:000}", problemyLista.Count,
                                                 różnica.Minutes, różnica.Seconds, różnica.Milliseconds));
            }
            catch (Exception)
            {
                DataTransition.Log("Anulowano działanie algorytmu Tabu");
                return new List<Rozwiązanie>();
            }
            finally
            {
                DataTransition.VisibilityChange(false);
            }
            return result.OrderBy(rozwiązanie => rozwiązanie.NazwaProblemu);
        }

        public static IEnumerable<Rozwiązanie> ObliczGA(List<Problem> problemyLista, CancellationToken token)
        {
            var nehStandard = new NEH.NehStandard(problemyLista);
            var nehResult = nehStandard.WygenerujeRozwiązania().ToList();
            var zipped = nehResult.Zip(problemyLista, (rozwiązanie, problem) => new { nehResult = rozwiązanie, problem });
            var result = new List<Rozwiązanie>();
            var parallelOptions = new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = Environment.ProcessorCount * 2 };
            DataTransition.VisibilityChange(true);
            try
            {
                var przed = DateTime.Now;
                Parallel.ForEach(zipped, parallelOptions, problem =>
                {
                    DataTransition.AddProblem(problem.problem.Nazwa);
                    DataTransition.Log(string.Format("Ga Start: {0}", problem.problem.Nazwa));
                    result.Add(GA.GA.WygenerujRozwiązanie(problem.nehResult, problem.problem));
                    DataTransition.RemoveProblem(problem.problem.Nazwa);
                    DataTransition.Log(string.Format("Ga End: {0}", problem.problem.Nazwa));
                    DataTransition.PostępChange();
                });
                var po = DateTime.Now;
                var różnica = po - przed;
                DataTransition.Log(string.Format("Obliczono rozwiązania dla {0} problemów. Co zajęło {1:00}:{2:00}:{3:000}", problemyLista.Count,
                                                 różnica.Minutes, różnica.Seconds, różnica.Milliseconds));
            }
            catch (Exception)
            {
                DataTransition.Log("Anulowano działanie algorytmu Ga");
                return new List<Rozwiązanie>();
            }
            finally
            {
                DataTransition.VisibilityChange(false);
            }
            return result.OrderBy(rozwiązanie => rozwiązanie.NazwaProblemu);
        }
    }
}
